package jp.co.systemi.asp.workflow;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;

import jp.co.systemi.asp.workflow.annotation.FormInput;

import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.validation.validator.AbstractValidator;

import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

public class Super extends WebPage {
    private static final Logger log = Logger.getLogger(Super.class.getName());

    private static ResourceBundle resources = ResourceBundle
            .getBundle(Super.class.getName().replace(".", "/"));
    protected WebMarkupContainer body;

    /** ボタン押下フラグ */
    protected boolean isSubmit = false;

    public Super() {
        body = new WebMarkupContainer("wi_body");
        add(body);
        /* ヘッダ部の設定 */
        // ログアウトリンク
        WebMarkupContainer logout = new WebMarkupContainer("wi_hdrLogout");
        UserService userService = UserServiceFactory.getUserService();
        WebRequest wr = (WebRequest) getRequestCycle().getRequest();
        HttpServletRequest sr = wr.getHttpServletRequest();
        String domain = sr.getServerName();

        String url = getRequestUrl(sr);
        log.info("url: " + url);
        url = userService.createLoginURL(url, domain);
        String logoutLink = userService.createLogoutURL(url, domain);
        log.info("logout URL: " + logoutLink);
        logout.add(new ExternalLink("wi_logoutLink", logoutLink));
        body.add(logout);

        com.google.appengine.api.users.User usr = userService.getCurrentUser();
        body.add(new Label("wi_user", usr.getEmail()));

        body.setVisible(true);

        // body.add(new BookmarkablePageLink<Void>("linkToTop", Top.class));
        /* コンテンツ部は子クラスで描画 */
    }

    @Override
    protected void setHeaders(WebResponse response) {
        response.setDateHeader("Expire", 0);
        super.setHeaders(response);
    }

    /**
     * Validatorを入力項目にバインディングする
     *
     * @param form
     */
    @SuppressWarnings("unchecked")
    protected void bindValidators(Form<?> form) {
        for (Field field : this.getClass().getDeclaredFields()) {
            log.fine(field.getName());
            try {
                // add @Input(入力項目) fields to form
                if (field.getAnnotation(FormInput.class) == null) {
                    continue;
                }
                FormComponent<?> formField = (FormComponent<?>) field.get(this);
                log.info("add field " + field.getName());
                try { // 間違いで、formの前に@FormInputを記述したら、下記のソースで例外が発生した。
                    formField.setRequired(false);
                } catch (Exception e) {
                    for (StackTraceElement msg : e.getStackTrace()) {
                        log.info(msg.getClassName() + ":" + msg.getMethodName()
                                + ":" + msg.getLineNumber());
                    }
                }
                form.add(formField);
                // Validatorを追加
                AbstractValidator<?> validator = null;
                for (Annotation a : field.getAnnotations()) {
                    try {
                        Method validatorClassMethod = a.getClass()
                                .getDeclaredMethod("validatorClass",
                                        (Class[]) null);
                        Class<?> validatorClass = (Class<?>) validatorClassMethod
                                .invoke(a);
                        // パラメータを収集
                        List<Object> params = new ArrayList<Object>();
                        for (Method method : a.getClass().getDeclaredMethods()) {
                            String paramName = method.getName().intern();
                            if (!"equals".equals(paramName)
                                    && !"hashCode".equals(paramName)
                                    && !"toString".equals(paramName)
                                    && !"annotationType".equals(paramName)
                                    && !"validatorClass".equals(paramName)
                                    && !"resourceKey".equals(paramName)) {
                                Object param = null;
                                try {
                                    param = method.invoke(a);
                                } catch (IllegalArgumentException e) {
                                    log.info(e.getLocalizedMessage());
                                } catch (InvocationTargetException e) {
                                    log.info(e.getLocalizedMessage());
                                }
                                params.add(param);
                            }
                        }
                        // パラメータあり
                        Class<?>[] paramTypes = new Class<?>[params.size()];
                        int i = 0;
                        for (Object param : params) {
                            if (param.getClass().equals(Integer.class)) {
                                paramTypes[i] = int.class;
                            } else {
                                paramTypes[i] = param.getClass();
                            }
                            i++;
                        }
                        Constructor<?> con = null;
                        try {
                            con = validatorClass.getConstructor(paramTypes);
                        } catch (SecurityException e) {
                            log.info(e.getLocalizedMessage());
                        } catch (NoSuchMethodException e) {
                            log.info(e.getLocalizedMessage());
                        }
                        try {
                            switch (params.size()) {
                            case 0:
                                validator = (AbstractValidator<?>) con
                                        .newInstance();
                                break;
                            case 1:
                                validator = (AbstractValidator<?>) con
                                        .newInstance(params.get(0));
                                break;
                            case 2:
                                validator = (AbstractValidator<?>) con
                                        .newInstance(params.get(0), params
                                                .get(1));
                                break;

                            }
                            log.info("add validator " + validator.toString());
                            formField.add((AbstractValidator) validator);

                        } catch (IllegalArgumentException e) {
                            log.info(e.getLocalizedMessage());
                        } catch (InvocationTargetException e) {
                            log.info(e.getLocalizedMessage());
                        }

                    } catch (SecurityException e) {
                        log.info(e.getLocalizedMessage());
                    } catch (NoSuchMethodException e) {
                        log.info(e.getLocalizedMessage());
                    } catch (IllegalArgumentException e) {
                        log.info(e.getLocalizedMessage());
                    } catch (IllegalAccessException e) {
                        log.info(e.getLocalizedMessage());
                    } catch (InvocationTargetException e) {
                        log.info(e.getLocalizedMessage());
                    } catch (InstantiationException e) {
                        log.info(e.getLocalizedMessage());
                    }
                }
            } catch (ClassCastException e) {
                log.info(e.getMessage());
            } catch (IllegalArgumentException e) {
                log.info(e.getMessage());
            } catch (IllegalAccessException e) {
                log.info(e.getMessage());
            }
        }
    }

    /**
     * Constructs and returns the full URL associated with the passed request
     * object.
     *
     * @param request
     *            Servlet request object with methods for retrieving the various
     *            components of the request URL
     */
    public static String getRequestUrl(HttpServletRequest request) {
        // バッチ、メールサブレットモード
        if (request.getServletPath().contains("_ah")) {
            return resources.getString("url");
        } else {
            StringBuilder requestUrl = new StringBuilder();
            String scheme = request.getScheme();
            int port = request.getLocalPort();
            log.info("port: " + port);

            requestUrl.append(scheme);
            requestUrl.append("://");
            requestUrl.append(request.getServerName());

            if (port != 0) { // Google上Stagingの時には、portは0になる
                if ((scheme.equals("http") && port != 80)
                        || (scheme.equals("https") && port != 443)) {
                    requestUrl.append(":");
                    requestUrl.append(port);
                }
            }

            requestUrl.append(request.getContextPath());
            requestUrl.append(request.getServletPath());

            return requestUrl.toString();
        }
    }
}
